<?php
declare(strict_types=1);

namespace app\admin\support\excel;

use PhpOffice\PhpSpreadsheet\Exception;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use think\helper\Str;
use think\Response;

class Export
{
    use MacroExcel;

    /**
     * @var ExcelContract $excel|null
     */
    protected ?ExcelContract $excel = null;

    protected $sheets;

    protected ?Spreadsheet $spreadsheet = null;

    protected string $extension = 'xlsx';

    /**
     * save
     *
     * @param ExcelContract $excel
     * @param string $filename
     * @return Response
     */
    public function save(ExcelContract $excel, string $filename = '')
    {
        $this->excel = $excel;

        // 如果有获取 extension 方法，直接调用
        if (method_exists($excel, 'getExtension')) {
            $this->extension = $excel->getExtension();
        }

        $this->init();
        $writer = Factory::make($this->extension, $this->spreadsheet);

        $filename = ($filename ? : Str::random(10)) . '.' . $this->extension;

        // 获取缓冲区数据
        ob_start();
        $writer->save('php://output');
        $streamData = ob_get_contents();
        ob_end_clean();

        // 创建响应对象并设置响应头
        return Response::create($streamData)
            ->contentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
            ->header([
                // 曝露头信息，允许客户端接受的时候使用头信息生成文件
                'Access-Control-Expose-Headers' => 'Content-Disposition,filename',
                'Content-Disposition' => 'attachment; filename="' . $filename . '"',
                'filename' => $filename
            ]);
    }

    /**
     * set extension
     *
     * @param $extension
     * @return static
     */
    public function setExtension($extension): static
    {
        $this->extension = $extension;

        return $this;
    }

    /**
     * init excel
     *
     * @throws Exception
     * @return void
     */
    protected function init(): void
    {
        $this->setMemoryLimit();
        // register worksheet for current excel
        $this->registerWorksheet();
        // before save excel
        $this->before();
        // set excel title
        $this->setTitle();
        // set excel headers
        $this->setExcelHeaders();
        // set cell width
        $this->setSheetWidth();
        // set worksheets
        $this->setWorksheets();
    }

    /**
     *  设置 sheets
     *
     * @throws Exception
     * @return void
     */
    protected function setWorksheets(): void
    {
        $keys= $this->getKeys();

        $isArray = $this->arrayConfirm();

        $worksheet = $this->getWorksheet();

        if (empty($keys)) {
            if ($isArray) {
                foreach ($this->excel->sheets() as $sheet) {
                    $worksheet->fromArray($sheet, null, $this->start . $this->row);
                    $this->incRow();
                }
            } else {
                foreach ($this->excel->sheets() as $sheet) {
                    $worksheet->fromArray($sheet->toArray(), null, $this->start . $this->row);
                    $this->incRow();
                }
            }
        } else {
            if ($isArray) {
                foreach ($this->excel->sheets() as $sheet) {
                    $worksheet->fromArray($this->getValuesByKeys($sheet, $keys), null, $this->start . $this->row);
                    $this->incRow();
                }
            } else {
                foreach ($this->excel->sheets() as $sheet) {
                    $worksheet->fromArray($this->getValuesByKeys($sheet->toArray(), $keys), null, $this->start . $this->row);
                    $this->incRow();
                }
            }
        }
    }

    /**
     * 判断 sheet 是否是 array 类型
     *
     * @return bool
     */
    protected function arrayConfirm(): bool
    {
        $sheets = $this->excel->sheets();

        $array = true;

        foreach ($sheets as $sheet) {
            $array = is_array($sheet);
            break;
        }

        return $array;
    }

    /**
     * 获取 item 特定 key 的值
     *
     * @param array $item
     * @param array $keys
     * @return array
     */
    protected function getValuesByKeys(array $item, array $keys): array
    {
        $array = [];

        foreach ($keys as $key) {
            $array[] = $item[$key];
        }

        return $array;
    }


    /**
     * 设置 Excel 头部
     *
     * @throws Exception
     */
    protected function setExcelHeaders(): void
    {
        $worksheet = $this->getWorksheet();

        // get columns
        $columns = $this->getSheetColumns();

        // get start row
        $startRow = $this->getStartRow();

        foreach ($this->excel->headers() as $k => $header) {
            $worksheet->getCell($columns[$k] . $startRow)->setValue($header);
        }

        $this->incRow();
    }

    /**
     *  get spreadsheet
     *
     * @return Spreadsheet|null
     */
    protected function getSpreadsheet(): ?Spreadsheet
    {
        if (!$this->spreadsheet) {
            $this->spreadsheet = new Spreadsheet();
        }

        return $this->spreadsheet;
    }

    /**
     * 获取 active sheet
     *
     * @throws Exception
     * @return Worksheet
     */
    protected function getWorksheet(): Worksheet
    {
        return $this->getSpreadsheet()->getActiveSheet();
    }
}
